/**
 * @file tcp_server.c
 * @brief 处理主要TCP任务
 * @version 0.1
 * @date 2020-01-22
 *
 * @copyright Copyright (c) 2020
 *
 */
#include <string.h>    // 字符串操作函数
#include <stdint.h>    // 标准整数类型定义
#include <sys/param.h> // 系统参数定义


#include "freertos/FreeRTOS.h"     // FreeRTOS操作系统头文件
#include "freertos/task.h"         // FreeRTOS任务相关头文件
#include "freertos/event_groups.h" // FreeRTOS事件组相关头文件
#include "esp_system.h"            // ESP32系统相关头文件
#include "esp_wifi.h"              // ESP32 WiFi相关头文件
#include "esp_event_loop.h"        // ESP32事件循环相关头文件
#include "esp_log.h"               // ESP32日志相关头文件
#include "nvs_flash.h"             // ESP32非易失性存储相关头文件

#include "lwip/err.h"     // LwIP错误处理相关头文件
#include "lwip/sockets.h" // LwIP套接字相关头文件
#include "lwip/sys.h"     // LwIP系统相关头文件
#include <lwip/netdb.h>   // LwIP网络数据库相关头文件

#include "wifi_configuration.h" //wifi配置头文件
#include "elaphureLink_protocol.h"//elaphureLink协议头文件
#include "usbip_server.h"//elaphureLink协议头文件
#include "dap_main.h"//elaphureLink协议头文件
const char *TAG = "tcp_server"; // 日志标签

extern TaskHandle_t kDAPTaskHandle; // 外部声明的DAP任务句柄
extern int kRestartDAPHandle;       // 外部声明的DAP重启标志


int kSock = -1; // 全局套接字描述符，初始化为-1

/**
 * @brief TCP服务器任务函数
 * @param pvParameters 任务参数（未使用）
 */
void tcp_server_task(void *pvParameters)
{
    uint8_t tcp_rx_buffer[1500] = {0};                    // 接收缓冲区，初始化为0
    char addr_str[128];                                   // 地址字符串缓冲区
    enum usbip_server_state_t usbip_state = WAIT_DEVLIST; // USB/IP服务器状态，初始化为等待设备列表
    uint8_t *data;                                        // 数据指针
    int addr_family;                                      // 地址族（IPv4/IPv6）
    int ip_protocol;                                      // IP协议类型
    int header;                                           // 协议头
    int ret, sz;                                          // 返回值和数据大小
    int on = 1; // 套接字选项开关，1表示启用
    //
    //初始化DAP处理
    pDAP_config_t pDAP_config = DAP_Pubilc_Init();

    while (1)   // 主循环
    {

#ifdef CONFIG_EXAMPLE_IPV4                                              // 如果配置为IPv4
        struct sockaddr_in destAddr;                                    // IPv4目标地址结构体
        destAddr.sin_addr.s_addr = htonl(INADDR_ANY);                   // 绑定到任意地址(监听设备上所有可用的网络接口)
        destAddr.sin_family = AF_INET;                                  // IPv4地址族
        destAddr.sin_port = htons(PORT);                                // 设置端口号，转换为网络字节序
        addr_family = AF_INET;                                          // 设置为IPv4地址族
        ip_protocol = IPPROTO_IP;                                       // 设置为IP协议
        inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1); // 将地址转换为字符串
#else                                                                   // IPV6                           // 否则配置为IPv6
        struct sockaddr_in6 destAddr;                                     // IPv6目标地址结构体
        bzero(&destAddr.sin6_addr.un, sizeof(destAddr.sin6_addr.un));     // 清零地址
        destAddr.sin6_family = AF_INET6;                                  // IPv6地址族
        destAddr.sin6_port = htons(PORT);                                 // 设置端口号，转换为网络字节序
        addr_family = AF_INET6;                                           // 设置为IPv6地址族
        ip_protocol = IPPROTO_IPV6;                                       // 设置为IPv6协议
        inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1); // 将地址转换为字符串
#endif

        // 创建套接字(SOCK_STREAM tcp类型，IP协议类型)
        int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
        if (listen_sock < 0) // 如果创建失败
        {
            ESP_LOGE(TAG, "Unable to create socket: errno %d\r\n", errno); // 打印错误信息
            break;                                                     // 跳出循环
        }
        ESP_LOGI(TAG, "Socket created\r\n"); // 打印套接字创建成功信息

        // 设置套接字选项：启用保活机制和禁用Nagle算法
        /*
            SO_KEEPALIVE：确保连接的有效性，自动清理失效连接，提高可靠性。
            TCP_NODELAY：禁用延迟优化，优先保证数据传输的实时性，适合小数据包频繁交互的场景。
        */
        setsockopt(listen_sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
        setsockopt(listen_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));

        // 绑定套接字
        int err = bind(listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
        if (err != 0) // 如果绑定失败
        {
            ESP_LOGE(TAG, "Socket unable to bind: errno %d\r\n", errno); // 打印错误信息
            break;                                                   // 跳出循环
        }
        ESP_LOGI(TAG, "Socket binded\r\n"); // 打印绑定成功信息

        // 监听套接字
        err = listen(listen_sock, 1); // 最大连接数为1
        if (err != 0)                 // 如果监听失败
        {
            ESP_LOGE(TAG, "Error occured during listen: errno %d\r\n", errno); // 打印错误信息
            break;                                                         // 跳出循环
        }
        ESP_LOGI(TAG, "Socket listening\r\n"); // 打印监听成功信息

#ifdef CONFIG_EXAMPLE_IPV6              // 如果配置为IPv6
        struct sockaddr_in6 sourceAddr; // IPv6源地址结构体
#else
        struct sockaddr_in sourceAddr; // IPv4源地址结构体
#endif
        uint32_t addrLen = sizeof(sourceAddr); // 地址结构体大小
        while (1)                              // 接受连接循环
        {
            // 接受连接
            kSock = accept(listen_sock, (struct sockaddr *)&sourceAddr, &addrLen);
            if (kSock < 0) // 如果接受失败
            {
                ESP_LOGE(TAG, "Unable to accept connection: errno %d\r\n", errno); // 打印错误信息
                break;                                                         // 跳出循环
            }
            // 设置套接字选项：启用保活机制和禁用Nagle算法
            setsockopt(kSock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
            setsockopt(kSock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));
            ESP_LOGI(TAG, "Socket accepted\r\n"); // 打印连接接受成功信息

            // 读取协议头（4字节）
            sz = 4;                   // 需要读取的字节数
            data = &tcp_rx_buffer[0]; // 指向缓冲区起始位置
            /* 读取协议头，直到读取完毕或出错 */
            do
            {
                ret = recv(kSock, data, sz, 0); // 接收数据
                if (ret <= 0)                   // 如果接收失败或连接关闭
                    goto cleanup;               // 跳转到清理部分
                sz -= ret;                      // 更新剩余需要读取的字节数
                data += ret;                    // 更新数据指针位置
            } while (sz > 0); // 循环直到读取完4字节

            // 解析协议头
            header = *((int *)(tcp_rx_buffer)); // 获取缓冲区前4字节作为协议头
            header = ntohl(header);             // 将网络字节序转换为主机字节序

            // 根据协议头类型处理不同协议
            if (header == EL_LINK_IDENTIFIER) // 如果是ElaphureLink协议
            {
                el_dap_work(tcp_rx_buffer, sizeof(tcp_rx_buffer)); // 调用DAP处理函数
            }
            else if ((header & 0xFFFF) == 0x8003 ||
                     (header & 0xFFFF) == 0x8005) // 如果是USB/IP协议（设备列表请求或导入请求）
            {
                if ((header & 0xFFFF) == 0x8005) // 如果是导入请求
                    usbip_state = WAIT_DEVLIST;  // 设置状态为等待设备列表
                else
                    usbip_state = WAIT_IMPORT;                                    // 设置状态为等待导入
                usbip_worker(tcp_rx_buffer, sizeof(tcp_rx_buffer), &usbip_state); // 调用USB/IP处理函数
            }
            else if (header == 0x47455420) // 如果是HTTP GET请求（ASCII码为"GET "）
            {
#ifdef CONFIG_USE_WEBSOCKET_DAP                                                // 如果配置了WebSocket DAP
                websocket_worker(kSock, tcp_rx_buffer, sizeof(tcp_rx_buffer)); // 调用WebSocket处理函数
#endif
            }
            else // 未知协议
            {
                ESP_LOGI(TAG, "Unknown protocol\n"); // 打印未知协议信息
            }

        cleanup:             // 清理标签
            if (kSock != -1) // 如果套接字有效
            {
                ESP_LOGE(TAG,"Shutting down socket and restarting...\r\n"); // 打印关闭套接字信息
                // shutdown(kSock, 0); // 关闭套接字（注释状态）
                close(kSock); // 关闭套接字

                // 释放DAP处理缓冲区
                el_process_buffer_free();

                // 重启DAP处理
                kRestartDAPHandle = RESET_HANDLE;    // 设置重启标志
                if (pDAP_config->kDAPTaskHandle)       // 如果DAP任务句柄有效
                    xTaskNotifyGive(pDAP_config->kDAPTaskHandle); // 通知DAP任务重启

                // shutdown(listen_sock, 0); // 关闭监听套接字（注释状态）
                // close(listen_sock);       // 关闭监听套接字（注释状态）
                // vTaskDelay(5);            // 延迟5个时钟周期（注释状态）
            }
        }
    }
    vTaskDelete(NULL); // 删除当前任务
}